package com.dhp.bkOjjudgeservice.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.dhp.bkOjfeignclient.service.QuestionFeignClient;
import com.dhp.bkOjjudgeservice.JudgeService;
import com.dhp.bkOjjudgeservice.codesandbox.CodeSandBox;
import com.dhp.bkOjjudgeservice.codesandbox.factory.CodeSandBocFactory;
import com.dhp.bkOjjudgeservice.strategy.JudgeStrategyManager;
import com.dhp.common.ErrorCode;
import com.dhp.exception.BusinessException;
import com.dhp.model.codesandbox.ExecuteRequest;
import com.dhp.model.codesandbox.ExecuteResponse;
import com.dhp.model.codesandbox.JudgeInfo;
import com.dhp.model.codesandbox.JudgeStrategyContext;
import com.dhp.model.dto.question.JudgeCase;
import com.dhp.model.entity.Question;
import com.dhp.model.entity.QuestionSubmit;
import com.dhp.model.enums.CodeSandBoxExecuteEnum;
import com.dhp.model.enums.JudgeInfoMessageEnum;
import com.dhp.model.enums.QuestionSubmitStatusEnum;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Service
@Data
@ConfigurationProperties(prefix = "code-sand-box")
@Slf4j
public class JudgeServiceImpl implements JudgeService {

    /**
     * 代码沙箱类型
     */
    private String type;

    @Resource
    private QuestionFeignClient questionFeignClient;

    /**
    @Resource
    private QuestionSubmitFeignClient questionFeignClient;
     **/

    @Resource
    private JudgeStrategyManager judgeStrategyManager;

    @Override
    @Transactional(rollbackFor = BusinessException.class)
    public QuestionSubmit doJudge(QuestionSubmit questionSubmit) {
        Long submitId = questionSubmit.getId();
        QuestionSubmit submitRecord = questionFeignClient.getSubmitById(submitId);
        if (submitRecord == null){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR , "不存在该提交记录");
        }

        //如果记录的状态已经为判题中，不再执行判题
        Integer submitStatus = submitRecord.getStatus();
        if (QuestionSubmitStatusEnum.RUNNING.getValue().equals(submitStatus)){
            throw new BusinessException(ErrorCode.OPERATION_ERROR , "正在判题，请勿重复提交");
        }

        //更新提交记录状态为判题中
        submitRecord.setStatus(QuestionSubmitStatusEnum.RUNNING.getValue());
        boolean update = questionFeignClient.updateById(submitRecord);
        if (!update){
            throw new BusinessException(ErrorCode.OPERATION_ERROR , "提交记录状态更新失败,等待中->判题中");
        }

        //获取测试用例和示例输出用例
        Long questionId = submitRecord.getQuestionId();
        Question question = questionFeignClient.getById(questionId);
        if (question == null){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR , "题目不存在，请联系管理员");
        }
        String judgeCase = question.getJudgeCase();
        List<JudgeCase> judgeCases = JSONUtil.toList(judgeCase, JudgeCase.class);
        List<String> inputList = new ArrayList<>();
        List<String> exOutPutList = new ArrayList<>();
        for (JudgeCase aCase : judgeCases) {
            inputList.add(aCase.getInput());
            exOutPutList.add(aCase.getOutput());
        }

        //调用代码沙箱
        ExecuteRequest executeRequest = new ExecuteRequest();
        executeRequest.setInputList(inputList);
        executeRequest.setCode(questionSubmit.getCode());
        executeRequest.setLanguage(questionSubmit.getLanguage());
        //工厂模式返回对应代码沙箱
        CodeSandBocFactory codeSandBocFactory = new CodeSandBocFactory();
        CodeSandBox codeSandBox = codeSandBocFactory.getCodeSandBox(type);
        ExecuteResponse executeResponse = codeSandBox.execute(executeRequest);
        //如果并没有执行成功
        if (!CodeSandBoxExecuteEnum.EXE_SUCCESS.getValue().equals(executeResponse.getStatus())){
            log.error("代码沙箱错误信息：" + executeResponse.getMsg());
            JudgeInfo errorInfo = new JudgeInfo();
            errorInfo.setMemory(0L);
            errorInfo.setTime(0L);
            if (CodeSandBoxExecuteEnum.COMPILE_FAILED.getValue().equals(executeResponse.getStatus())){
                errorInfo.setMessage(JudgeInfoMessageEnum.COMPILE_ERROR.getValue());
                errorInfo.setErrorMessage(getSafeErrorMessage(executeResponse.getMsg()));
            }else {
                errorInfo.setMessage(JudgeInfoMessageEnum.RUNTIME_ERROR.getValue());
                errorInfo.setErrorMessage(executeResponse.getMsg());
            }
            QuestionSubmit errorResult = new QuestionSubmit();
            BeanUtil.copyProperties(questionSubmit , errorResult);
            errorResult.setStatus(QuestionSubmitStatusEnum.FAIL.getValue());
            errorResult.setJudgeInfo(JSONUtil.toJsonStr(errorInfo));
            boolean upgrade = questionFeignClient.updateById(errorResult);
            if(!upgrade){
                throw new BusinessException(ErrorCode.OPERATION_ERROR , "题目状态更新失败，判题中->判题失败");
            }
            return errorResult;
        }

        //判断执行结果
        QuestionSubmit questionSubmitResult = new QuestionSubmit();
        BeanUtil.copyProperties(questionSubmit , questionSubmitResult);
        List<String> outputList = executeResponse.getOutputList();
        JudgeInfo judgeInfo = executeResponse.getJudgeInfo();
        //默认状态成功
        questionSubmitResult.setStatus(QuestionSubmitStatusEnum.SUCCEED.getValue());

        //执行判题服务
        JudgeStrategyContext judgeStrategyContext = new JudgeStrategyContext();
        judgeStrategyContext.setInputList(inputList);
        judgeStrategyContext.setOutputList(exOutPutList);
        judgeStrategyContext.setExecuteOutputList(outputList);
        judgeStrategyContext.setJudgeInfo(judgeInfo);
        judgeStrategyContext.setQuestion(question);
        judgeStrategyContext.setQuestionSubmit(questionSubmitResult);
        judgeStrategyManager.doJudge(judgeStrategyContext);
        //判题后更新judgeInfo
        questionSubmitResult.setJudgeInfo(JSONUtil.toJsonStr(judgeInfo));

        //更新提交记录
        boolean upgrade = questionFeignClient.updateById(questionSubmitResult);
        if(!upgrade){
            throw new BusinessException(ErrorCode.OPERATION_ERROR , "题目状态更新失败，判题中->判题成功");
        }
        //如果提交AC，则更新题目通过数
        if (JudgeInfoMessageEnum.ACCEPTED.getValue().equals(judgeInfo.getMessage())){
            question.setAcceptedNum(question.getAcceptedNum() + 1);
            boolean isQuestionUpdated = questionFeignClient.updateQuestionById(question);
            if (!isQuestionUpdated){
                throw new BusinessException(ErrorCode.OPERATION_ERROR , "题目信息通过数更新失败");
            }
        }
        return questionSubmitResult;
    }


    public String getSafeErrorMessage(String errorMessage){
        if (StringUtils.isEmpty(errorMessage)){
            return "";
        }
        int index = errorMessage.indexOf(":");
        return errorMessage.substring(index + 1);
    }

}
